using System;
using System.Reflection;
using Microsoft.SPOT;
using NetduinoHappyHour.Hardware;

namespace NetduinoHappyHour.Util
{
    /// <summary>
    /// generic class to update objects by giving property and value in string format
    /// Was introduced not to require recompile and deploy to modify behaviours
    /// </summary>
   public class UpdateObject : IExecutable
    {
        public Object ObjectToUpdate { set; get; }
        private string _propertyToUpdate;
        private object[] _parameter;
        private static char[] SINGLE_VALUE = new char[1] { ';' };
        private static char[] MULTI_VALUE = new char[1] { '#' };
        private string[] _manyPropertiesToUpdate;
        
        /// <summary>
        /// base constructor, updates almost any object respective its properties
        /// </summary>
        /// <param name="ObjectToUpdate">The Object to update Property(ies)</param>
        /// <param name="PropertyChainToUpdate">"PropertyToBeUpdated;ValueToBeSet;TypeOfProperty#NextPropertyToBeUpdated;Value;Type"</param>
        public UpdateObject(Object ObjectToUpdate, string PropertyChainToUpdate)
        {
            this.ObjectToUpdate = ObjectToUpdate;
            _manyPropertiesToUpdate = PropertyChainToUpdate.Split(MULTI_VALUE);            
        }

        /// <summary>
        /// base constructor, updates almost any object respective its properties
        /// </summary>
        /// <param name="ObjectToUpdate">the object to be updated</param>
        /// <param name="PropertyToUpdate">the objects property to be updated</param>
        /// <param name="Value">the value to be set</param>
        /// <param name="Type">the type, currently supported: int, bool, string</param>
        public UpdateObject(Object ObjectToUpdate, string PropertyToUpdate, string Value, string Type)
        {
            this.ObjectToUpdate = ObjectToUpdate;
            _propertyToUpdate = PropertyToUpdate;
            _parameter = new object[1];
            _parameter[0] = CreateInstanceFromString(Value, Type);
        }

        /// <summary>
        /// performs the update
        /// </summary>
        public void Execute()
        {
            if (ObjectToUpdate != null)
            {
                if (_propertyToUpdate != null)
                {
                    UpdateProperty(ObjectToUpdate, _propertyToUpdate, _parameter);
                }
                else
                {
                    object [] param = new object[1];
                    foreach (string item in _manyPropertiesToUpdate)
                    {
                        string[] props = item.Split(SINGLE_VALUE);
                        if (props != null && props.Length == 3)
                        {
                            param[0] = CreateInstanceFromString(props[1], props[2]);
                            UpdateProperty(ObjectToUpdate, props[0], param);
                        }
                        else
                        {
                            // TODO check error handling, improve error feedback to remote client
                            throw new InvalidOperationException("Property: " + _propertyToUpdate + " is invalid");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Value"></param>
        /// <param name="Type"></param>
        /// <returns></returns>
        private object CreateInstanceFromString(string Value, string Type)
        {
            object returnValue;
            // TODO check
            switch (Type)
            {
                case "int":
                    returnValue = (object)int.Parse(Value);
                    break;
                case "bool":
                    returnValue = (object)BooleanHelper.Parse(Value);
                    break;
                // assume default to be string
                default:
                    returnValue = Value;
                    break;
            }

            return returnValue;
        }

        /// <summary>
        /// Performs update of objects property by given value
        /// </summary>
        /// <param name="MyObject">Object to be updated</param>
        /// <param name="MyProperty">Name of Property to be updated as string</param>
        /// <param name="Value">The object to be set, type needs to fit to type of property</param>
        private void UpdateProperty(object MyObject, string MyProperty, object[] Value)
        {
            MethodInfo mi;
            if (MyObject != null)
            {
                mi = MyObject.GetType().GetMethod("set_"+MyProperty);
                if (mi != null)
                {
                    mi.Invoke(ObjectToUpdate, Value);
                }
                else
                {
                    // TODO check error handling, improve error feedback to remote client
                    throw new InvalidOperationException("Property: " + _propertyToUpdate + " is invalid");
                }
            }
        }
    }
}
